
package com.woodblockwithoutco.remotecontrollerexample;

import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

import com.woodblockwithoutco.remotecontrollerexample.TransportControlView.ArtworkChangeListener;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Xfermode;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.RemoteControlClient;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

public class TransportControlView extends LinearLayout {
    private static final boolean DEBUG = true;

    private static final String TAG = "TransportControlView";

    private static final int RESET_TO_METADATA_DELAY = 5000;

    private static final int CHECK_MUSIC_STATE_INTERVAL_MS = 1000;

    private static final int WHAT_CHECK_MUSIC_STATE = 0;

    private TextView mTrackTitle;

    private TextView mTrackArtistAlbum;

    private ImageView mBtnPrev;

    private ImageView mBtnPlay;

    private ImageView mBtnNext;
    
    private ImageView mBadge;

    private Metadata mMetadata = new Metadata();

    private int mTransportControlFlags;

    private int mCurrentPlayState;

    private boolean mBound;

    private TransportControlService mRCService;
    
    private MediaButtonController mMediaButtonController;

    private AudioManager mAudioManager;

    private boolean mVisible;

    private boolean mStarted;

    private boolean mRunning;

    private boolean mMetadataSupported;

    private boolean mAttached;

    private Handler mHandler;
    
    private final ArrayList<WeakReference<ArtworkChangeListener>> mCallbacks = new ArrayList<WeakReference<ArtworkChangeListener>>();

    private Handler.Callback mCallback = new Handler.Callback() {

        @Override
        public boolean handleMessage(Message msg) {
            boolean handled = false;
            switch (msg.what) {
                case WHAT_CHECK_MUSIC_STATE:
                    if (mRunning) {
                        handleCheckMusicState();
                        mHandler.sendEmptyMessageDelayed(WHAT_CHECK_MUSIC_STATE,
                                CHECK_MUSIC_STATE_INTERVAL_MS);
                    }
                    handled = true;
                    break;

                default:
                    break;
            }
            return handled;
        }

    };

    /**
     * The metadata which should be populated into the view once we've been
     * attached
     */
    private Bundle mPopulateMetadataWhenAttached = null;

    private OnClientUpdateListener mRCClientUpdateListener = new OnClientUpdateListener() {
        @Override
        public void onClientChange(boolean clearing) {
            if (clearing) {
                clearMetadata();
            }
        }

        @Override
        public void onClientPlaybackStateUpdate(int state) {
            updatePlayPauseState(state);
        }

        @Override
        public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs,
                long currentPosMs, float speed) {
            updatePlayPauseState(state);
            if (DEBUG)
                Log.d(TAG, "onClientPlaybackStateUpdate(state=" + state + ", stateChangeTimeMs="
                        + stateChangeTimeMs + ", currentPosMs=" + currentPosMs + ", speed=" + speed
                        + ")");

        }

        @Override
        public void onClientTransportControlUpdate(int transportControlFlags) {
            updateTransportControls(transportControlFlags);
        }

        @Override
        public void onClientMetadataUpdate(Bundle metadata) {
            updateMetadata(metadata);
        }
    };

    private final Runnable mResetToMetadata = new Runnable() {
        public void run() {
            resetToMetadata();
        }
    };

    private final OnClickListener mTransportCommandListener = new OnClickListener() {
        public void onClick(View v) {
            int keyCode = -1;
            if (v == mBtnPrev) {
                keyCode = KeyEvent.KEYCODE_MEDIA_PREVIOUS;
            } else if (v == mBtnNext) {
                keyCode = KeyEvent.KEYCODE_MEDIA_NEXT;
            } else if (v == mBtnPlay) {
                keyCode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
            }
            if (keyCode != -1) {
                sendMediaButtonClick(keyCode);
                delayResetToMetadata(); // if the scrub bar is showing, keep
                                        // showing it.
            }
        }
    };

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            // Getting the binder and activating RemoteController instantly
            TransportControlService.RCBinder binder = (TransportControlService.RCBinder) service;
            mRCService = binder.getService();
            boolean permissionGranted = mRCService.setRemoteControllerEnabled();
            if (permissionGranted) {
                mBound = true;
                mRCService.setClientUpdateListener(mRCClientUpdateListener);
                mMetadataSupported = true;
                updateRunning();
            }
            setArtworkConfiguration();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mBound = false;
        }
    };

    public TransportControlView(Context context, AttributeSet attrs) {
        super(context, attrs);
        if (isInEditMode()) return;
        if (DEBUG) Log.v(TAG, "Create TCV " + this);
        mCurrentPlayState = RemoteControlClient.PLAYSTATE_STOPPED;
        mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
        mMediaButtonController = new MediaButtonController(context);
        HandlerThread handlerThread = new HandlerThread(TAG,
                android.os.Process.THREAD_PRIORITY_BACKGROUND);
        handlerThread.start();
        mHandler = new Handler(handlerThread.getLooper(), mCallback);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (isInEditMode()) return;
        mBadge = (ImageView) findViewById(R.id.badge);
        mTrackTitle = (TextView) findViewById(R.id.title);
        mTrackArtistAlbum = (TextView) findViewById(R.id.artist_album);
        mBtnPrev = (ImageView) findViewById(R.id.btn_prev);
        mBtnPlay = (ImageView) findViewById(R.id.btn_play);
        mBtnNext = (ImageView) findViewById(R.id.btn_next);
        final View buttons[] = {
                mBtnPrev, mBtnPlay, mBtnNext
        };
        for (View view : buttons) {
            view.setOnClickListener(mTransportCommandListener);
        }
        // final boolean screenOn =
        // KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
        // setEnableMarquee(screenOn);
    }

    // called in HandlerThread
    private void handleCheckMusicState() {
        final boolean musicActive = mAudioManager.isMusicActive();
        post(new Runnable() {

            @Override
            public void run() {
                updatePlayPauseState(musicActive ? RemoteControlClient.PLAYSTATE_PLAYING
                        : RemoteControlClient.PLAYSTATE_STOPPED);
            }
        });
    }
    
    /**
     * Remove the given observer's callback.
     *
     * @param callback The callback to remove
     */
    public void removeArtworkChangeListener(ArtworkChangeListener callback) {
        if (DEBUG) Log.v(TAG, "*** unregister callback for " + callback);
        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
            if (mCallbacks.get(i).get() == callback) {
                mCallbacks.remove(i);
            }
        }
    }

    /**
     * Register to receive notifications about general keyguard information
     * (see {@link InfoCallback}.
     * @param callback The callback to register
     */
    public void registerArtworkChangeListener(ArtworkChangeListener callback) {
        if (DEBUG) Log.v(TAG, "*** register callback for " + callback);
        // Prevent adding duplicate callbacks
        for (int i = 0; i < mCallbacks.size(); i++) {
            if (mCallbacks.get(i).get() == callback) {
                if (DEBUG) Log.e(TAG, "Object tried to add another callback",
                        new Exception("Called by"));
                return;
            }
        }
        mCallbacks.add(new WeakReference<ArtworkChangeListener>(callback));
        removeArtworkChangeListener(null); // remove unused references
        sendUpdates(callback);
    }
    
    private void sendUpdates(ArtworkChangeListener callback) {
        callback.onSetBackground(mMetadata.bitmap);
    }

    public void start() {
        mStarted = true;
        updateRunning();
    }

    public void stop() {
        mStarted = false;
        updateRunning();
    }

    private void updateRunning() {
        boolean running = mVisible && mStarted && !mMetadataSupported;
        if (DEBUG)
            Log.v(TAG, "updateRunning(), mVisible: " + mVisible + ", mStarted: " + mStarted
                    + ", mMetadataSupported: " + mMetadataSupported + ", running: " + running);
        if (running != mRunning) {
            if (running) {
                handleCheckMusicState();
                mHandler.sendEmptyMessageDelayed(WHAT_CHECK_MUSIC_STATE,
                        CHECK_MUSIC_STATE_INTERVAL_MS);
            } else {
                mHandler.removeMessages(WHAT_CHECK_MUSIC_STATE);
            }
            mRunning = running;
        }
    }

    @Override
    protected void onWindowVisibilityChanged(int visibility) {
        super.onWindowVisibilityChanged(visibility);
        mVisible = visibility == View.VISIBLE;
        if (DEBUG)
            Log.v(TAG, "onWindowVisibilityChanged(), mVisible: " + mVisible);
        updateRunning();
    }

    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (isInEditMode()) return;
        if (DEBUG) Log.v(TAG, "onAttachToWindow()");
        if (!mAttached) {
            mAttached = true;
            if (hasKitKatApi()) {
                if (mPopulateMetadataWhenAttached != null) {
                    updateMetadata(mPopulateMetadataWhenAttached);
                    mPopulateMetadataWhenAttached = null;
                }
                if (DEBUG)
                    Log.v(TAG, "Registering TCV " + this);
                mMetadata.clear();
                Intent intent = new Intent(
                        "com.woodblockwithoutco.remotecontrollerexample.BIND_RC_CONTROL_SERVICE");
                getContext().bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
                // KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitor);
            }
        }
    }
    
    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        setArtworkConfiguration();
    }
    
    private boolean setArtworkConfiguration() {
        if (mBound) {
            final DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
            final int dim = Math.max(dm.widthPixels, dm.heightPixels);
            return mRCService.setArtworkConfiguration(dim, dim);
        } else {
            return false;
        }
    }

    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (isInEditMode()) return;
        if (DEBUG) Log.v(TAG, "onDetachFromWindow()");
        if (mAttached) {
            mAttached = false;
            if (hasKitKatApi()) {
                if (DEBUG)
                    Log.v(TAG, "Unregistering TCV " + this);

                if (mBound) {
                    mRCService.setRemoteControllerDisabled();
                }
                getContext().unbindService(mConnection);
                mMetadata.clear();
                // KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitor);
            }
        }
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        SavedState ss = new SavedState(super.onSaveInstanceState());
        ss.artist = mMetadata.artist;
        ss.trackTitle = mMetadata.trackTitle;
        ss.albumTitle = mMetadata.albumTitle;
        ss.bitmap = mMetadata.bitmap;
        return ss;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (!(state instanceof SavedState)) {
            super.onRestoreInstanceState(state);
            return;
        }
        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());
        mMetadata.artist = ss.artist;
        mMetadata.trackTitle = ss.trackTitle;
        mMetadata.albumTitle = ss.albumTitle;
        mMetadata.bitmap = ss.bitmap;
        populateMetadata();
    }

    private void setEnableMarquee(boolean enabled) {
        if (DEBUG)
            Log.v(TAG, (enabled ? "Enable" : "Disable") + " transport text marquee");
        if (mTrackTitle != null)
            mTrackTitle.setSelected(enabled);
        if (mTrackArtistAlbum != null)
            mTrackTitle.setSelected(enabled);
    }

    static class SavedState extends BaseSavedState {
        boolean clientPresent;

        String artist;

        String trackTitle;

        String albumTitle;
        
        Bitmap bitmap;

        SavedState(Parcelable superState) {
            super(superState);
        }

        private SavedState(Parcel in) {
            super(in);
            clientPresent = in.readInt() != 0;
            artist = in.readString();
            trackTitle = in.readString();
            albumTitle = in.readString();
            bitmap = Bitmap.CREATOR.createFromParcel(in);
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeInt(clientPresent ? 1 : 0);
            out.writeString(artist);
            out.writeString(trackTitle);
            out.writeString(albumTitle);
            if (bitmap != null) {
                bitmap.writeToParcel(out, flags);
            }
        }

        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }

    private void sendMediaButtonClick(int keyCode) {
        // TODO We should think about sending these up/down events accurately
        // with touch up/down
        // on the buttons, but in the near term this will interfere with the
        // long press behavior.
        if (mBound) {
            mRCService.sendKeyEvent(keyCode);
        } else {
            mMediaButtonController.sendMediaKeyEvent(keyCode);
        }
    }



    private void updateTransportControls(int transportControlFlags) {
        mTransportControlFlags = transportControlFlags;
    }

    private static void setVisibilityBasedOnFlag(View view, int flags, int flag) {
        if ((flags & flag) != 0) {
            view.setVisibility(View.VISIBLE);
        } else {
            view.setVisibility(View.INVISIBLE);
        }
    }

    private void updatePlayPauseState(int state) {
        if (DEBUG)
            Log.v(TAG, "updatePlayPauseState(), old=" + mCurrentPlayState + ", state=" + state);
        if (state == mCurrentPlayState) {
            return;
        }
        final int imageResId;
        switch (state) {
            case RemoteControlClient.PLAYSTATE_ERROR:
                imageResId = android.R.drawable.stat_sys_warning;
                break;

            case RemoteControlClient.PLAYSTATE_PLAYING:
                imageResId = android.R.drawable.ic_media_pause;
                break;

            // case RemoteControlClient.PLAYSTATE_BUFFERING:
            // imageResId = android.R.drawable.ic_media_stop;
            // break;

            case RemoteControlClient.PLAYSTATE_PAUSED:
            default:
                imageResId = android.R.drawable.ic_media_play;
                break;
        }

        mBtnPlay.setImageResource(imageResId);
        mCurrentPlayState = state;
    }

    void resetToMetadata() {
        // do nothing
    }

    void delayResetToMetadata() {
        removeCallbacks(mResetToMetadata);
        postDelayed(mResetToMetadata, RESET_TO_METADATA_DELAY);
    }
    
    void setBadgeIcon(Drawable bmp) {
        mBadge.setImageDrawable(bmp);

        if (hasKitKatApi()) {
            final ColorMatrix cm = new ColorMatrix();
            cm.setSaturation(0);
            mBadge.setColorFilter(new ColorMatrixColorFilter(cm));
            setXfermodeForImageView(mBadge, new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
            mBadge.setImageAlpha(0xef);
        }
    }
    
    private static void setXfermodeForImageView(ImageView imageView, Xfermode mode) {
        if (hasKitKatApi()) {
            Method method = ReflectionUtils.findMethod(ImageView.class, "setXfermode", Xfermode.class);
            ReflectionUtils.invokeMethod(method, imageView, mode);
        }
    }

    public static class Metadata {
        public static final String METADATA_KEY_ALBUMARTIST = "artist";
        public static final String METADATA_KEY_TITLE = "track_title";
        public static final String METADATA_KEY_ALBUM = "album_title";
        public static final String BITMAP_KEY_ARTWORK = "bitmap_artwork";

        private String artist;

        private String trackTitle;

        private Bitmap bitmap;

        private String albumTitle;

        public void clear() {
            artist = null;
            trackTitle = null;
            albumTitle = null;
            bitmap = null;
        }

        @Override
        public String toString() {
            return "Metadata [artist=" + artist + ", trackTitle=" + trackTitle + ", albumTitle="
                    + albumTitle + "]";
        }

    }

    void clearMetadata() {
        mPopulateMetadataWhenAttached = null;
        mMetadata.clear();
        populateMetadata();
    }

    void updateMetadata(Bundle metadata) {
        if (isAttachedToWindow()) {
            mMetadata.artist = metadata.getString(Metadata.METADATA_KEY_ALBUMARTIST,
                    mMetadata.artist);
            mMetadata.trackTitle = metadata.getString(Metadata.METADATA_KEY_TITLE,
                    mMetadata.trackTitle);
            mMetadata.albumTitle = metadata.getString(Metadata.METADATA_KEY_ALBUM,
                    mMetadata.albumTitle);
            mMetadata.bitmap = metadata.getParcelable(Metadata.BITMAP_KEY_ARTWORK);
            populateMetadata();
        } else {
            mPopulateMetadataWhenAttached = metadata;
        }
    }

    /**
     * Populates the given metadata into the view
     */
    private void populateMetadata() {
        final String remoteClientPackage = mRCService.getRemoteControlClientPackageName();
        Drawable badgeIcon = null;
        try {
            badgeIcon = getContext().getPackageManager().getApplicationIcon(remoteClientPackage);
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "Couldn't get remote control client package icon", e);
        }
        setBadgeIcon(badgeIcon);

        mTrackTitle.setText(!TextUtils.isEmpty(mMetadata.trackTitle) ? mMetadata.trackTitle : null);

        final StringBuilder sb = new StringBuilder();
        if (!TextUtils.isEmpty(mMetadata.artist)) {
            if (sb.length() != 0) {
                sb.append(" - ");
            }
            sb.append(mMetadata.artist);
        }
        if (!TextUtils.isEmpty(mMetadata.albumTitle)) {
            if (sb.length() != 0) {
                sb.append(" - ");
            }
            sb.append(mMetadata.albumTitle);
        }

        final String trackArtistAlbum = sb.toString();
        mTrackArtistAlbum.setText(!TextUtils.isEmpty(trackArtistAlbum) ? trackArtistAlbum : null);

        dispatchSetBackground(mMetadata.bitmap);

        final int flags = mTransportControlFlags;
        setVisibilityBasedOnFlag(mBtnPrev, flags, RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS);
        setVisibilityBasedOnFlag(mBtnNext, flags, RemoteControlClient.FLAG_KEY_MEDIA_NEXT);
        setVisibilityBasedOnFlag(mBtnPlay, flags, RemoteControlClient.FLAG_KEY_MEDIA_PLAY
                | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE
                | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
                | RemoteControlClient.FLAG_KEY_MEDIA_STOP);

        updatePlayPauseState(mCurrentPlayState);
    }

    private void dispatchSetBackground(Bitmap bmp) {
        if (DEBUG) Log.d(TAG, "dispatchSetBackground");
        final int count = mCallbacks.size();
        for (int i = 0; i < count; i++) {
            ArtworkChangeListener cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onSetBackground(bmp);
            }
        }
    }

    public static interface OnClientUpdateListener {
        void onClientChange(boolean clearing);

        void onClientTransportControlUpdate(int transportControlFlags);

        void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs, long currentPosMs,
                float speed);

        void onClientPlaybackStateUpdate(int state);

        void onClientMetadataUpdate(Bundle metadata);
    }
    
    public static interface ArtworkChangeListener {
        void onSetBackground(Bitmap bmp);
    }
    

    private static boolean hasKitKatApi() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
    }

    private static class MediaButtonController {
        // @hide Constants in frameworks/base/core/java/android/provider/Settings.java
        private static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver";

        private Context mContext;

        private Object mInstanceAudioService;

        private Method mMethodDispatchMediaKeyEvent;
        
        private boolean mInit;

        public MediaButtonController(Context context) {
            mContext = context.getApplicationContext();
        }

        public void sendMediaKeyEvent(int keyCode) {
            if (!dispatchMediaKeyEvent(keyCode)) {
                sendBroadcastMediaButtonClick(keyCode);
            }
        }
        
        private void sendBroadcastMediaButtonClick(int keyCode) {
            Log.d(TAG, "sendBroadcastMediaButtonClick E, keyCode: " + keyCode);
            String packageName = getMediaButtonReceiverPackageName();
            Log.d(TAG, "getMediaButtonReceiverPackageName: " + packageName);

            Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
            keyIntent.setPackage(packageName);

            // magic argument values from decompiled CM Locker
            KeyEvent keyEvent = new KeyEvent(65535L, 200L, KeyEvent.ACTION_DOWN, keyCode, 0, 0, 248, 226, 8);
            keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
            if (hasKitKatApi()) {
                mContext.sendBroadcast(keyIntent);
            } else {
                mContext.sendOrderedBroadcast(keyIntent, null);
            }
            try {
                Thread.sleep(200L);
            } catch (InterruptedException e) {
                // ignore
            }

            // magic argument values from decompiled CM Locker
            keyEvent = new KeyEvent(65535L, 200L, KeyEvent.ACTION_UP, keyCode, 0, 0, 248, 0, 8);
            keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
            if (hasKitKatApi()) {
                mContext.sendBroadcast(keyIntent);
            } else {
                mContext.sendOrderedBroadcast(keyIntent, null);
            }
        }

        private String getMediaButtonReceiverPackageName() {
            String mediaButtonReceiver = Settings.System.getString(mContext.getContentResolver(),
                    MEDIA_BUTTON_RECEIVER);
            if (TextUtils.isEmpty(mediaButtonReceiver)) {
                return null;
            }

            ComponentName cn = ComponentName.unflattenFromString(mediaButtonReceiver);
            if (cn == null) {
                return null;
            }

            return cn.getPackageName();
        }

        private void init() {
            try {
                Class<?> clazzAudioManager = ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)).getClass();
                Field fieldsService = clazzAudioManager.getDeclaredField("sService");
                fieldsService.setAccessible(true);
                mInstanceAudioService = fieldsService.get(null);

                Class<?> clazzIAudioService = Class.forName("android.media.IAudioService");
                mMethodDispatchMediaKeyEvent = clazzIAudioService.getDeclaredMethod(
                        "dispatchMediaKeyEvent", new Class[] {
                            KeyEvent.class
                        });
            } catch (ClassNotFoundException e) {
                Log.e(TAG, "Error init", e);
            } catch (NoSuchMethodException e) {
                Log.e(TAG, "Error init", e);
            } catch (NoSuchFieldException e) {
                Log.e(TAG, "Error init", e);
            } catch (IllegalAccessException e) {
                Log.e(TAG, "Error init", e);
            } catch (IllegalArgumentException e) {
                Log.e(TAG, "Error init", e);
            }
        }

        private boolean dispatchMediaKeyEvent(int keyCode) {
            if (!mInit) {
                init();
                mInit = true;
            }
            Log.d(TAG, "dispatchMediaKeyEvent E, keyCode: " + keyCode);
            if (mInstanceAudioService == null || mMethodDispatchMediaKeyEvent == null) {
                return false;
            }
            try {
                // magic argument values from decompiled CM Locker
                Object[] args1 = new Object[] {new KeyEvent(65535L, 200L, KeyEvent.ACTION_DOWN, keyCode, 0, 0, 248, 226, 8)};
                mMethodDispatchMediaKeyEvent.invoke(mInstanceAudioService, args1);

                try {
                    Thread.sleep(200L);
                } catch (InterruptedException e) {
                    // ignore
                }

                // magic argument values from decompiled CM Locker
                Object[] args2 = new Object[] {new KeyEvent(65535L, 200L, KeyEvent.ACTION_UP, keyCode, 0, 0, 248, 0, 8)};
                mMethodDispatchMediaKeyEvent.invoke(mInstanceAudioService, args2);
                return true;
            } catch (IllegalAccessException e) {
                Log.e(TAG, "Error dispatchMediaKeyEvent", e);
            } catch (IllegalArgumentException e) {
                Log.e(TAG, "Error dispatchMediaKeyEvent", e);
            } catch (InvocationTargetException e) {
                Log.e(TAG, "Error dispatchMediaKeyEvent", e);
            }
            return false;
        }
    }
}
